#include"udp_client.h"
#include<signal.h>
#include<string.h>
#include<strings.h>
#include<arpa/inet.h>

void client_read_callback(event_loop *loop, int fd, void *args){
    udp_client* server = (udp_client *)args;
    
    // 处理读业务
    server->do_read();
}


// 构造函数
udp_client::udp_client(event_loop* loop, const char* ip, uint16_t port){
    
	//1.创建socket
	_sockfd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, IPPROTO_UDP);
	if(_sockfd==-1){
		fprintf(stderr,"udp_client:socker error!\n");
		exit(1);
	}
	
	// 2.初始化要连接的服务器地址
	bzero(&_server_addr, sizeof(_server_addr));
	_server_addr.sin_family = AF_INET;   
	inet_aton(ip,&_server_addr.sin_addr);
	_server_addr.sin_port = htons(port);

    // 3.连接服务器
    int ret = connect(_sockfd, (struct sockaddr*)&_server_addr, sizeof(_server_addr));
	if(ret == -1){
        perror("udp_client connect error");
        exit(1);
    }

	// 4.将形参loop添加到udp_server的_loop中
	_loop = loop;

     // 5.监控_sockfd的读事件
	_loop->add_io_event(_sockfd, client_read_callback, EPOLLIN, this);

    printf("UDP client is running id:%s\tport:%d\n",ip, port);


}

// 重写发送数据的函数
int udp_client::send_message(const char *data, int msglen, int msgid){
    printf("udp client send id:%d len:%d message:%s\n",msgid, msglen, data);

    // udp client 主动发数据给服务器
    if(msglen > MESSAGE_LENGTH_LIMIT){
        fprintf(stderr, "client:udp message format error too large");
       return -1;
    }

    // 设置数据包头
    msg_head head;
    head.msgid = msgid;
    head.msglen = msglen;

    // 设置数据包体
    memcpy(_write_buf, &head, MESSAGE_HEAD_LEN);
    memcpy(_write_buf+MESSAGE_HEAD_LEN, data, msglen);

    int ret = sendto(_sockfd, _write_buf, msglen+MESSAGE_HEAD_LEN, 0, NULL, 0);
    // int ret = sendto(_sockfd, _write_buf, msglen+MESSAGE_HEAD_LEN, 0, (struct sockaddr*)&_server_addr, _addrlen);
    if(ret == -1){
        fprintf(stderr, "udp client sendto error\n");
        return -1;
    }

    return ret;
}

// 注册一个msgid和回调函数的路由关系
void udp_client::add_msg_router(int msgid, msg_callback* cb, void* args){
    _router.register_msg_router(msgid, cb, args);
}

// 处理读业务
void udp_client::do_read(){
    char client_IP[1024]; 		 //客户端IP

    while(true){

        // 1.尝试读数据
        int pkg_len = recvfrom(_sockfd, &_read_buf,   sizeof(_read_buf), 0, (struct sockaddr *)&_server_addr, &_addrlen);
        if(pkg_len == -1){
            if(errno==EINTR){    //中断错误
				fprintf(stderr,"udp_client:recvfrom=EINTR!\n");
				continue;
			}
			else if(errno==EAGAIN){  //非阻塞错误
				fprintf(stderr,"udp_client:recvfrom=EAGAIN!\n");
				break;
			}
			else{
				fprintf(stderr,"udp_client:recvfrom error!\n");
				break;
			}
        }
        else{
            //输出客户端IP 端口
			printf("read:server ip:%s port:%d\n",
                inet_ntop(AF_INET, &_server_addr.sin_addr.s_addr, client_IP, sizeof(client_IP)),   
                ntohs(_server_addr.sin_port));         // 根据accept传出参数，获取客户端 ip 和 port 
   
            // 2.先读头部信息，得到msgid和msglen
            msg_head head;
            memcpy(&head, _read_buf, MESSAGE_HEAD_LEN);
            if(head.msglen>MESSAGE_LENGTH_LIMIT || head.msglen<0 || head.msglen + MESSAGE_HEAD_LEN != pkg_len){
                // 报文格式有问题
                fprintf(stderr,"udp message format error too large or too small\n");
		        break;
            }

            // 3.已经得到了完整的数据包，通过msgid调用对应的回调业务
            _router.call(head.msgid, _read_buf+MESSAGE_HEAD_LEN, head.msglen, this);

        }

    }
}

// 析构函数
udp_client::~udp_client(){
    _loop->del_io_event(_sockfd);
    close(_sockfd);
}